package server;

/**
 * Location cache for users.
 * @author Jeff
 */
import iface.HvZUser;
import iface.Location;
import iface.Logger;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.Map;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;

public class UserCache {
    public enum CacheType {
        HUMAN, ZOMBIE;
    }

    private Map<String, HvZUser> cache;
    private CacheType type;

    /**
     * Constructs a new UserCache
     * 
     * @param type
     *            the type of the cache
     */
    public UserCache(CacheType type) {
        this.type = type;
        cache = new ConcurrentHashMap<String, HvZUser>();
    }

    /**
     * @return the cache of type Map<String, HvZUser>
     */
    public Map<String, HvZUser> getCacheMap() {
        return cache;
    }

    /**
     * @return a list of the values
     */
    public List<HvZUser> getCacheValues() {
        // Return a copy of the values, this must be a copy so that we can
        // serialize it.
        return new ArrayList<HvZUser>(cache.values());
    }

    /**
     * Checks if the given user is already cached
     * 
     * @param username
     *            the user to check
     * @return true if the user is cached, false otherwise
     */
    public boolean isUserCached(String username) {
        return cache.containsKey(username);
    }

    /**
     * Removes and returns the given user from the cache
     * 
     * @param username
     *            the user to remove
     * @return the user that is removed, null if the user is not in the cache
     */
    public HvZUser removeUser(String username) {
        return cache.remove(username);
    }

    /**
     * Updates a user's location
     * 
     * @param loc
     *            the location data
     */
    public void updateLocation(Location loc) {
        HvZUser user = cache.get(loc.getUsername());
        if (user == null) {
            Logger.error("Attempted to update location data for user that was not cached.");
        } else {
            user.updateLocation(loc);
            cache.put(loc.getUsername(), user);
        }
    }

    /**
     * Adds a new user to the cache
     * 
     * @param user
     *            the user to add
     */
    public void addUser(HvZUser user) {
        if (type == CacheType.HUMAN) {
            if (user.isZombie())
                Logger.error("Attempting to add a zombie to a human cache! "
                        + user);
            else
                cache.put(user.getUsername(), user);
        } else if (type == CacheType.ZOMBIE) {
            if (user.isZombie())
                cache.put(user.getUsername(), user);
            else
                Logger.error("Attempting to add a human to a zombie cache! "
                        + user);
        }
    }

    /**
     * Removes from the cache all users that have not recently updated their
     * locations
     */
    public void cleanCache() {
        Iterator<HvZUser> iter = cache.values().iterator();
        while (iter.hasNext()) {
            HvZUser user = iter.next();
            int freshness = user.noLocationUpdate();
            if (freshness <= 0) {
                Logger.verbose("Removing stale user: " + user);
                iter.remove();
                Logger.verbose("Status of map: " + cache);
            }
        }
    }

}
